﻿using System;
using System.Collections.Generic;
using System.Data;
using Loki.Resources;
using Loki.Utils;

namespace Loki.Data
{
    /// <summary>
    /// Base class for command mappers.
    /// </summary>
    public abstract class CommandMapper : LokiObject
    {
        #region Properties set by spring
        /// <summary>
        /// Gets or sets the database adapter.
        /// </summary>
        public IDatabaseAdapter Database { get; set; }

        public bool LogQueries { get; set; }
        #endregion

        /// <summary>
        /// Prepare command by mapping parameters.
        /// </summary>
        /// <param name="P_CommandName">Command name.</param>
        /// <param name="P_MapParameters">Parameter map.</param>
        protected IDatabaseCommand PrepareCommand(string P_CommandName, Action<IDatabaseCommand> P_MapParameters)
        {
            IDatabaseCommand L_Command = Database.CreateCommand(P_CommandName);
            if (P_MapParameters != null)
            {
                P_MapParameters(L_Command);
            }

            return L_Command;
        }

        protected IDatabaseCommand PrepareCommand(string P_CommandName)
        {
            return PrepareCommand(P_CommandName, null);
        }

        /// <summary>
        /// Executes the command on the database (no return).
        /// </summary>
        /// <param name="P_Transaction">Transaction on the database.</param>
        /// <param name="P_CommandName">Command name.</param>
        /// <param name="P_MapParameters">Command parameters map.</param>
        public virtual void ExecuteNonQuery(IDbTransaction P_Transaction, string P_CommandName, Action<IDatabaseCommand> P_MapParameters)
        {
            IDatabaseCommand L_Command = PrepareCommand(P_CommandName, P_MapParameters);
            try
            {
                if (LogQueries)
                {
                    Log.DebugFormat(LogStrings.Mapper_QueryExecute, L_Command.Dump());
                }

                Database.ExecuteNonQuery(L_Command, P_Transaction);
            }
            catch (Exception Ex)
            {
                throw BuildErrorFormat<LokiException>(ErrorMessages.Data_Mappers_QueryError, Ex, this.GetType().Name, L_Command.Dump());
            }
        }

        /// <summary>
        /// Executes the command on the database (scalar return).
        /// </summary>
        /// <typeparam name="TObjectType">Return type.</typeparam>
        /// <param name="P_Transaction">Transaction on the database.</param>
        /// <param name="P_CommandName">Command name.</param>
        /// <param name="P_MapParameters">Command parameters map.</param>
        public virtual TObjectType ExecuteScalar<TObjectType>(IDbTransaction P_Transaction, string P_CommandName, Action<IDatabaseCommand> P_MapParameters)
        {
            IDatabaseCommand L_Command = PrepareCommand(P_CommandName, P_MapParameters);
            try
            {
                if (LogQueries)
                {
                    Log.DebugFormat(LogStrings.Mapper_QueryExecute, L_Command.Dump());
                }

                return (TObjectType)Database.ExecuteScalar(L_Command, P_Transaction);
            }
            catch (Exception Ex)
            {
                throw BuildErrorFormat<LokiException>(ErrorMessages.Data_Mappers_QueryError, Ex, this.GetType().Name, L_Command.Dump());
            }
        }

        protected static List<TData> BuildList<TData>(IDataReader P_Reader, Func<IDataReader, TData> P_Creator, Action<TData, IDataReader> P_Loader)
        {
            List<TData> L_List = new List<TData>();
            try
            {
                while (P_Reader.Read())
                {
                    TData L_Temp = P_Creator(P_Reader);
                    P_Loader(L_Temp, P_Reader);
                    L_List.Add(L_Temp);
                }
            }
            finally
            {
                P_Reader.Dispose();
            }

            return L_List;
        }

        protected static TData BuildOne<TData>(IDataReader P_Reader, Func<IDataReader, TData> P_Creator, Action<TData, IDataReader> P_Loader)
        {
            TData L_Return = default(TData);
            try
            {
                if (P_Reader.Read())
                {
                    L_Return = P_Creator(P_Reader);
                    P_Loader(L_Return, P_Reader);
                }
            }
            finally
            {
                P_Reader.Dispose();
            }

            return L_Return;
        }
    }
}
